package com.example.demo28_cas_client1.config;

import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;

@Configuration
public class CASSecurityConfig {

    @Autowired
    CASServerProperties casServerProperties;
    @Autowired
    CASClientProperties casClientProperties;
    @Autowired
    UserDetailsService userDetailsService;

    /**
     * 接下来配置 ServiceProperties，ServiceProperties 中主要配置一下 Client 的登录地址即可，
     * 这个地址就是在 CAS Server 上登录成功后，重定向的地址。
     */
    @Bean
    ServiceProperties serviceProperties(){
        ServiceProperties serviceProperties = new ServiceProperties();
        serviceProperties.setService(casClientProperties.getLogin());
        return serviceProperties;
    }

    /**
     * CasAuthenticationEntryPoint 则是 CAS 验证的入口，这里首先设置 CAS Server 的登录地址，
     * 同时将前面的 ServiceProperties 设置进去，这样当它登录成功后，就知道往哪里跳转了。
     */
    @Bean
    @Primary
    CasAuthenticationEntryPoint casAuthenticationEntryPoint(){
        CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
        entryPoint.setLoginUrl(casServerProperties.getLogin());
        entryPoint.setServiceProperties(serviceProperties());
        return entryPoint;
    }

    /**
     * TicketValidator 这是配置 ticket 校验地址，CAS Client 拿到 ticket 要去 CAS Server 上校验，
     * 默认校验地址是：https://cas.wangnian.org:8443/cas/proxyValidate?ticket=xxx
     */
    @Bean
    TicketValidator ticketValidator(){
        return new Cas20ProxyTicketValidator(casServerProperties.getPrefix());
    }

    /**
     * CasAuthenticationProvider 主要用来处理 CAS 验证逻辑，关于 AuthenticationProvider 在前面和大家分享过（SpringSecurity 自定义认证逻辑的两种方式(高级玩法)），
     * 当时就说，想要自定义认证逻辑，如短信登录等，都可以通过扩展 AuthenticationProvider 来实现，这里的 CAS 登录当然也不例外，
     * 这里虽然设置了一个 userDetailService，但是目的不是为了从数据库中查询数据做校验，因为登录是在 CAS Server 中进行的，这个的作用，我在后面会做介绍。
     */
    @Bean
    CasAuthenticationProvider casAuthenticationProvider(){
        CasAuthenticationProvider provider = new CasAuthenticationProvider();
        provider.setServiceProperties(serviceProperties());
        provider.setTicketValidator(ticketValidator());
        provider.setUserDetailsService(userDetailsService);
        provider.setKey("wangnian");
        return provider;
    }

    /**
     * CasAuthenticationFilter 则是 CAS 认证的过滤器，过滤器将请求拦截下来之后，交由 CasAuthenticationProvider 来做具体处理。
     */
    @Bean
    CasAuthenticationFilter casAuthenticationFilter(CasAuthenticationProvider casAuthenticationProvider){
        CasAuthenticationFilter filter = new CasAuthenticationFilter();
        filter.setServiceProperties(serviceProperties());
        filter.setAuthenticationManager(new ProviderManager(casAuthenticationProvider));
        return filter;
    }

    /**
     * SingleSignOutFilter 表示接受 CAS Server 发出的注销请求，
     * 所有的注销请求都将从 CAS Client 转发到 CAS Server，CAS Server 处理完后，会通知所有的 CAS Client 注销登录。
     */
    @Bean
    SingleSignOutFilter singleSignOutFilter(){
        SingleSignOutFilter outFilter = new SingleSignOutFilter();
        outFilter.setIgnoreInitConfiguration(true);
        return outFilter;
    }

    /**
     * LogoutFilter 则是配置将注销请求转发到 CAS Server。
     */
    @Bean
    LogoutFilter logoutFilter(){
        LogoutFilter logoutFilter = new LogoutFilter(casServerProperties.getLogout(),new SecurityContextLogoutHandler());
        logoutFilter.setFilterProcessesUrl(casClientProperties.getLogoutRelative());
        return logoutFilter;
    }

}
